/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.server.scm;

import java.util.Collections;
import java.util.Set;
import org.internna.iwebjtracker.project.model.IWebJTrackerUser;
import org.internna.iwebjtracker.project.model.Module;
import org.internna.iwebjtracker.project.model.Project;
import org.internna.iwebjtracker.project.model.Version;
import org.internna.iwebjtracker.project.validation.Errors;
import org.internna.iwebjtracker.project.validation.ProjectTypeValidator;
import org.internna.iwebjtracker.server.AbstractConnectionManagerServer;
import org.internna.iwebjtracker.server.connection.ConnectionDetails;
import org.internna.iwebjtracker.server.connection.ServerConnection;
import org.internna.iwebjtracker.server.scm.model.ItemData;
import org.internna.iwebjtracker.server.scm.model.ItemInfo;
import org.internna.iwebmvc.utils.Assert;
import org.internna.iwebmvc.utils.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import static org.internna.iwebmvc.utils.StringUtils.hasText;
import static org.internna.iwebjtracker.project.validation.Errors.*;

/**
 * Convenience implementation of some basic SCM functionality.
 *
 * @author Jose Noheda
 * @since 1.0
 */
public abstract class AbstractSourceControlManagementServer<T extends ServerConnection, C extends ConnectionDetails, M extends Module<P, M>, V extends Version<U, P, V>, U extends IWebJTrackerUser, P extends Project<C, M, V, U, P>, I extends ItemInfo<C, M, V, U, P>> extends AbstractConnectionManagerServer<T, C> implements SourceControlManagementServer<T, C, M, V, U, P, I> {

    @Autowired private Set<ProjectTypeValidator<C, M, V, U, P, I>> projectTypeValidators;

    /**
     * Test the connection and proceeds to get the root folder.
     *
     * @param project a non null project
     * @return null or a known error
     */
    @Override public final Errors validateProject(P project) {
        boolean check = testConnection(project.getConnectionDetails());
        if (check) {
            I info = getItemInfo(project, null);
            check = info != null && info.exists();
            if (check) {
                check = info.isDirectory();
                if (check) {
                    return invokeProjectTypeValidation(project);
                }
                return PROJECT_NOT_A_FOLDER;
            }
            return PROJECT_DOES_NOT_EXIST;
        }
        return CONNECTION_FAILED;
    }

    /**
     * Loops through all configured project type validators trying to find one that accepts
     * this type of project. If it can find it then launches the validation.
     *
     * @param project a not null project
     * @return the abstraction of the error found (null if there wasn't any)
     */
    protected final Errors invokeProjectTypeValidation(P project) {
        if (CollectionUtils.isNotEmpty(projectTypeValidators)) {
            for (ProjectTypeValidator<C, M, V, U, P, I> validator : projectTypeValidators) {
                if (validator.accept(project.getProjectType())) {
                    return validator.validateProject(this, project);
                }
            }
        }
        return null;
    }

    /**
     * Obtains the item info and proceeds to get its children closing the connection at the end.
     */
    @Override public final Set<I> getSubItems(P project, String absolutePath) {
        Assert.notNull(project);
        T connection = openConnection(project.getConnectionDetails());
        Set<I> subitems = getSubItems(connection, project, getItemInfo(connection, project, absolutePath));
        closeConnection(connection);
        return subitems;
    }

    /**
     * Obtains a new connection and proceeds to fetch the children closing the connection at the end.
     */
    @Override public final Set<I> getSubItems(P project, I itemInfo) {
        T connection = openConnection(project.getConnectionDetails());
        Set<I> subitems = getSubItems(connection, project, itemInfo);
        closeConnection(connection);
        return subitems;
    }

    /**
     * Checks if the item can hold children before proceed.
     */
    protected final Set<I> getSubItems(T connection, P project, I itemInfo) {
        Assert.notNull(itemInfo);
        return itemInfo.isDirectory() ? doGetSubItems(connection, project, itemInfo) : Collections.EMPTY_SET;
    }

    /**
     * Obtains a new connection and proceeds to fetch the item closing the connection at the end.
     */
    @Override public final I getItemInfo(P project, String absolutePath) {
        T connection = openConnection(project.getConnectionDetails());
        I info = getItemInfo(connection, project, absolutePath);
        closeConnection(connection);
        return info;
    }

    /**
     * Obtains a new connection and proceeds to load the contents closing the connection at the end.
     */
    @Override public final ItemData loadItem(I item) {
        ItemData data = null;
        if (item != null) {
            T connection = openConnection(item.getProject().getConnectionDetails());
            data = loadItem(connection, item);
            closeConnection(connection);
        }
        return data;
    }

    /**
     * Obtains a new connection and proceeds to load the contents assuming ASCII data closing the connection at the end.
     */
    @Override public final ItemData loadItem(P project, String url) {
        ItemData data = null;
        if (project != null) {
            T connection = openConnection(project.getConnectionDetails());
            data = loadItem(connection, project, url, false);
            closeConnection(connection);
        }
        return data;
    }

    /**
     * Check if item is present and data already loaded before continuing.
     *
     * @param item anything
     * @return the contents if the item exists.
     */
    @Override public final ItemData loadItem(T connection, I item) {
        ItemData data = null;
        if ((item != null) && (item.exists())) {
            String mime = item.getMimeType();
            data = item.getData() == null ? loadItem(connection, item.getProject(), item.getAbsolutePath(), !hasText(mime) || (!mime.startsWith("text"))) : item.getData();
            item.setData(data);
        }
        return data;
    }

    /**
     * Implemented by plugin to actually obtain the item info.
     */
    protected abstract I getItemInfo(T connection, P project, String absolutePath);

    /**
     * Implemented by plugin to actually obtain the sub-items.
     */
    protected abstract Set<I> doGetSubItems(T connection, P project, I itemInfo);

}
